"""
什么是闭包
定义双层嵌套函数，内层函数可以访问外层函数的变量【对内层函数来说该变量是外部的，对于外层函数来说该变量反而是内部的】
将内层函数作为外层函数的返回，此内层函数就是闭包函数

闭包：
    1. 函数嵌套
    2. 内部函数使用了外部函数的变量
    3. 返回的是内部函数

优点：
- 无需定义全局变量即可实现通过内部函数，持续的访问、修改某个值
- 闭包使用的变量是存在于外部函数内，对于外部函数来说它又是个临时变量，因此难以被错误的调用修改

缺点：
- 由于内部函数持续引用外部函数的值，所以会导致这一部分内存空间不被释放，一直占用内存

【案例】
银行存钱、取钱，如果使用全局变量定义余额的话，那么该余额会有被别人篡改的风险

nonlocal关键字的作用
在闭包函数（内部函数中）想要修改外部函数的变量值的话，需要用nonlocal声明这个外部变量
"""

# print("==================================== 全局变量演示")
# # 全局变量，可以被其他方法、函数访问或修改、不安全
# yu_e = 0  # 账户余额
#
#
# def atm(num, operation_type=True):
#     global yu_e
#     if operation_type:
#         yu_e += num
#         print(f"存钱：+{num}，余额：{yu_e}")
#     else:
#         yu_e -= num
#         print(f"取钱：-{num}，余额：{yu_e}")
#
#
# atm(100)
# atm(200)
# atm(20, False)

# print("==================================== 简单的闭包函数演示")
#
#
# def outer(logo):
#     def inner(msg):
#         """
#         闭包函数，依赖外部的变量
#         :param logo 来自外部(上层)函数的变量，内部作用域是可以访问外部的成员的哈
#         :param msg: 来自外部函数的变量
#         :return:
#         """
#         print(f"<{logo}>{msg}<{logo}>")
#
#     # 返回内部函数
#     return inner
#
#
# func1 = outer("黑马程序员")  # 通过传入固定值，得到返回的函数(实际是返回的内部的inner函数)
# func1("大家好")  # 实际调用时，inner函数使用的是持续存在的变量：黑马程序员
# func1("你们好")
#
# func2 = outer("传智教育")
# func2("老师好")  # 实际调用时，inner函数使用的是持续存在的变量：传智教育，这样就做到了访问不了别人的变量
# func2("学生好")


# print("==================================== 闭包函数中修改外部函数的变量演示")
#
#
# def outer(num1):
#     def inner(num2):
#         """
#         :param num1: num1变量只是临时的，别人是无法访问num1的，内部函数使用时也得通过特殊方式
#         :param num2:
#         :return:
#         """
#         nonlocal num1
#         num1 += num2  # 必须通过关键字nonlocal修饰外部变量，否则在内部函数中只能访问，不能修改
#         print(num1)
#
#     # 返回内部函数
#     return inner
#
#
# func1 = outer(1)
# func1(1)
# func1(1)

print("==================================== 闭包实现：ATM存取钱")


# 最终实现了：我要调用atm方法，而atm方法又得到了yu_e变量，而yu_e又是个临时的外部变量，这样通过闭包的方式就实现了防止别人篡改
# 调用ATM方法，访问yu_e变量，而它此时已不再是全局变量，而是函数的临时变量，别人无法访问
def account_create(yu_e=0):
    """
    yu_e 不再是全局变量，而是作用于account_create函数内的一个临时变量，别人操作不了该变量，别人用时，只能用他自己创建的变量
    """

    def atm(num, deposit=True):
        nonlocal yu_e  # 必须通过关键字nonlocal修饰外部变量，否则在内部函数中只能访问，不能修改
        if deposit:
            yu_e += num
            print(f"存钱：+{num}，余额：{yu_e}")
        else:
            yu_e -= num
            print(f"取钱：-{num}，余额：{yu_e}")

    return atm


atm = account_create(0)
atm(100)
atm(200)
atm(40, False)
